import { expect, type Locator } from "@playwright/test";
import { interactWithTimeRangeMenu } from "@rilldata/web-common/tests/utils/explore-interactions.ts";
import { test } from "./setup/base";

// These tests are highly dependent on comparisons. So we need to fix that with rill time. Will be in a separate PR
test.describe.serial("Alerts", () => {
  test.describe.serial("Alerts with filters", () => {
    test("Should create alert with filters", async ({ adminPage }) => {
      await adminPage.goto("/e2e/openrtb/explore/auction_explore");

      await adminPage.getByRole("button", { name: "Create alert" }).click();

      const alertForm = adminPage.locator("form#create-alert-form");

      // Select "Last 6 hours" as time range
      await interactWithTimeRangeMenu(alertForm, async () => {
        await alertForm.getByRole("menuitem", { name: "Last 6 hours" }).click();
      });
      // Enable time comparison
      await alertForm.getByLabel("Toggle time comparison").click();

      // Select "App Site Name" as split by dimension
      await adminPage
        .getByRole("combobox", { name: "Split by dimension" })
        .click();
      await adminPage.getByRole("listbox").getByText("App Site Name").click();

      // Preview should have the correct 1st row
      await assertRowValues(alertForm, 0, [
        "Not Available",
        "77600",
        "-16600",
        "-18%",
      ]);

      // Go to criteria tab
      await alertForm.getByRole("button", { name: "Next" }).click();

      // Set criteria type to % change
      const criteria0 = alertForm.getByLabel("criteria-0");
      await criteria0.getByLabel("Criteria type").click();
      await adminPage
        .getByRole("listbox")
        .getByText("% change from previous period")
        .click();

      // Preview should be updated
      await assertRowValues(alertForm, 0, [
        "My Little Universe",
        "4600",
        "3200",
        "229%",
      ]);

      // Add another criteria with % change < 100
      await alertForm.getByRole("button", { name: "+ Add Criteria" }).click();
      const criteria1 = alertForm.getByLabel("criteria-1");
      // Set criteria type to % change
      await criteria1.getByLabel("Criteria type").click();
      await adminPage
        .getByRole("listbox")
        .getByText("% change from previous period")
        .click();
      // Set operator to `<`
      await criteria1.getByLabel("Criteria operator").click();
      await adminPage
        .getByRole("listbox")
        .getByText("<", { exact: true })
        .click();
      // Set the value to 100
      await criteria1.getByTitle("Criteria value").fill("100");

      // Preview should be updated
      await assertRowValues(alertForm, 0, ["FuboTV", "2600", "1200", "86%"]);

      // Go to delivery tab
      await alertForm.getByRole("button", { name: "Next" }).click();

      // Name should be autogenerated
      await expect(alertForm.getByTitle("Alert name")).toHaveValue(
        "Requests % change vs previous period alert",
      );
      // Update the name
      await alertForm
        .getByTitle("Alert name")
        .fill("Requests for App Site Name % change vs previous period alert");

      // Create the alert
      await alertForm.getByRole("button", { name: "Create" }).click();

      // Notification is shown
      await expect(adminPage.getByLabel("Notification")).toHaveText(
        " Alert created Go to alerts  ",
      );
      // Clicking "Go to alerts" takes us to the alerts page
      await adminPage.getByRole("link", { name: "Go to alerts" }).click();

      // Go to the newly created alert
      await adminPage
        .getByRole("link", {
          name: "Requests for App Site Name % change vs previous period alert",
        })
        .click();

      // Assert that alert is created with correct fields
      // Assert alert name
      await expect(adminPage.getByLabel("Alert name")).toHaveText(
        "Requests for App Site Name % change vs previous period alert",
      );
      // Assert alert dashboard
      await expect(adminPage.getByLabel("Alert dashboard name")).toHaveText(
        "Dashboard Programmatic Ads Auction",
      );
      // Assert alert filters
      await expect(adminPage.getByLabel("Alert filters")).toHaveText(
        /Filters \(1\)\s*Last 6 hours\s*vs\s*Previous period/m,
      );
      // Assert alert criteria
      await expect(adminPage.getByLabel("Alert criteria")).toHaveText(
        /Criteria\s*requests\s*% change from previous period\s*> 0%\s*requests\s*% change from previous period\s*< 100%/m,
      );
    });

    test("Should edit alert with filters", async ({ adminPage }) => {
      await adminPage.goto("/e2e/openrtb/-/alerts");

      await adminPage
        .getByRole("link", {
          name: "Requests for App Site Name % change vs previous period alert",
        })
        .click();

      // Edit the alert
      await adminPage.getByRole("button", { name: "Edit" }).click();

      const alertForm = adminPage.locator("form#edit-alert-form");

      // Select "Last 24 hours" as time range
      await interactWithTimeRangeMenu(alertForm, async () => {
        await alertForm
          .getByRole("menuitem", { name: "Last 24 hours" })
          .click();
      });

      // Add "Ad Size" filter
      await alertForm.getByLabel("Add filter button").click();
      await alertForm.getByRole("menuitem", { name: "Ad Size" }).click();
      // Add filters for 1024x768, 120x600, 160x600
      await alertForm.getByRole("menuitem", { name: "1024x768" }).click();
      await alertForm.getByRole("menuitem", { name: "120x600" }).click();
      await alertForm.getByRole("menuitem", { name: "160x600" }).click();
      await alertForm.getByLabel("Open ad_size filter").click();

      // Update split by dimension to "App Site Domain"
      await adminPage
        .getByRole("combobox", { name: "Split by dimension" })
        .click();
      await adminPage.getByRole("listbox").getByText("App Site Domain").click();

      // Preview should have the correct 1st row
      await assertRowValues(alertForm, 0, [
        "Not Available",
        "10600",
        "1400",
        "15%",
      ]);

      // Go to criteria tab
      await alertForm.getByRole("button", { name: "Next" }).click();

      // Set 1st criteria to < 0
      const criteria0 = alertForm.getByLabel("criteria-0");
      // Set operator to `<`
      await criteria0.getByLabel("Criteria operator").click();
      await adminPage
        .getByRole("listbox")
        .getByText("<", { exact: true })
        .click();

      // Set group operation to "or"
      await alertForm.getByLabel("Criteria group operation").click();
      await adminPage.getByRole("listbox").getByText("or").click();

      // Set 2nd criteria to > 100
      const criteria1 = alertForm.getByLabel("criteria-1");
      // Set operator to `>`
      await criteria1.getByLabel("Criteria operator").click();
      await adminPage
        .getByRole("listbox")
        .getByText(">", { exact: true })
        .click();

      // Preview should have the correct 1st row
      await assertRowValues(alertForm, 0, [
        "fastpeoplesearch.com",
        "2000",
        "1800",
        "900%",
      ]);

      // Go to criteria tab
      await alertForm.getByRole("button", { name: "Next" }).click();

      // Update the name
      await alertForm
        .getByTitle("Alert name")
        .fill("Requests for App Site Domain % change vs previous period alert");

      // Update the alert
      await alertForm.getByRole("button", { name: "Update" }).click();

      // Notification is shown
      await expect(adminPage.getByLabel("Notification")).toHaveText(
        "Alert edited",
      );

      // Assert that alert is updated with correct fields
      // Assert alert name
      await expect(adminPage.getByLabel("Alert name")).toHaveText(
        "Requests for App Site Domain % change vs previous period alert",
      );
      // Assert alert dashboard
      await expect(adminPage.getByLabel("Alert dashboard name")).toHaveText(
        "Dashboard Programmatic Ads Auction",
      );
      // Assert alert filters
      await expect(adminPage.getByLabel("Alert filters")).toHaveText(
        /Filters \(2\)\s*Last 24 hours\s*vs\s*Previous period\s*Ad Size\s*1024x768\s*\+2 others/m,
      );
      // Assert alert criteria
      await expect(adminPage.getByLabel("Alert criteria")).toHaveText(
        /Criteria\s*requests\s*% change from previous period\s*< 0%\s*requests\s*% change from previous period\s*> 100%/m,
      );
    });

    test("Should delete alert with filters", async ({ adminPage }) => {
      await adminPage.goto("/e2e/openrtb/-/alerts");

      await adminPage
        .getByRole("link", {
          name: "Requests for App Site Domain % change vs previous period alert",
        })
        .click();

      // Delete the alert
      await adminPage.getByLabel("Alert context menu").click();
      await adminPage.getByRole("menuitem", { name: "Delete Alert" }).click();

      // Back to listing page without any alerts
      await expect(
        adminPage.getByText("You don't have any alerts yet"),
      ).toBeVisible();
    });
  });

  test.describe.serial("Alerts with schedule", () => {
    test("Should create alert with schedule", async ({ adminPage }) => {
      await adminPage.goto("/e2e/openrtb/explore/auction_explore");

      await adminPage.getByRole("button", { name: "Create alert" }).click();

      const alertForm = adminPage.locator("form#create-alert-form");

      // Select "App Site Name" as split by dimension
      await adminPage
        .getByRole("combobox", { name: "Split by dimension" })
        .click();
      await adminPage.getByRole("listbox").getByText("App Site Name").click();

      // Go to criteria tab
      await alertForm.getByRole("button", { name: "Next" }).click();
      // Go to delivery tab
      await alertForm.getByRole("button", { name: "Next" }).click();

      // Toggle to "Set schedule"
      await alertForm.getByRole("button", { name: "Set schedule" }).click();

      // Set as a daily alert
      await alertForm.getByLabel("Frequency").click();
      await adminPage.getByRole("option", { name: "Daily" }).click();
      // Set to run at 10:00 pm
      await alertForm.getByLabel("Time", { exact: true }).click();
      await adminPage.getByRole("option", { name: "10:00 PM" }).click();

      // Create the alert
      await alertForm.getByRole("button", { name: "Create" }).click();

      // Notification is shown
      await expect(adminPage.getByLabel("Notification")).toHaveText(
        " Alert created Go to alerts  ",
      );
      // Clicking "Go to alerts" takes us to the alerts page
      await adminPage.getByRole("link", { name: "Go to alerts" }).click();

      // Go to the newly created alert
      await adminPage
        .getByRole("link", {
          name: "Requests value alert",
        })
        .click();

      // Assert that alert is created with correct fields
      // Assert alert name
      await expect(adminPage.getByLabel("Alert name")).toHaveText(
        "Requests value alert",
      );
      // Assert alert dashboard
      await expect(adminPage.getByLabel("Alert dashboard name")).toHaveText(
        "Dashboard Programmatic Ads Auction",
      );
      // Assert alert schedule
      await expect(adminPage.getByLabel("Alert schedule")).toHaveText(
        /Schedule\s+At 10:00 PM, every day/m,
      );
    });

    test("Should edit alert with schedule", async ({ adminPage }) => {
      await adminPage.goto("/e2e/openrtb/-/alerts");

      await adminPage
        .getByRole("link", {
          name: "Requests value alert",
        })
        .click();

      // Edit the alert
      await adminPage.getByRole("button", { name: "Edit" }).click();

      const alertForm = adminPage.locator("form#edit-alert-form");

      // Go to criteria tab
      await alertForm.getByRole("button", { name: "Next" }).click();
      // Go to criteria tab
      await alertForm.getByRole("button", { name: "Next" }).click();

      // Set as a monthly alert
      await alertForm.getByLabel("Frequency").click();
      await adminPage.getByRole("option", { name: "Monthly" }).click();

      // Update the alert
      await alertForm.getByRole("button", { name: "Update" }).click();

      // Notification is shown
      await expect(adminPage.getByLabel("Notification")).toHaveText(
        "Alert edited",
      );

      // Assert that alert is updated with correct schedule
      await expect(adminPage.getByLabel("Alert schedule")).toHaveText(
        /Schedule\s+At 10:00 PM, on the 1st of each month/m,
      );
    });

    test("Should delete alert with schedule", async ({ adminPage }) => {
      await adminPage.goto("/e2e/openrtb/-/alerts");

      await adminPage
        .getByRole("link", {
          name: "Requests value alert",
        })
        .click();

      // Delete the alert
      await adminPage.getByLabel("Alert context menu").click();
      await adminPage.getByRole("menuitem", { name: "Delete Alert" }).click();

      // Back to listing page without any alerts
      await expect(
        adminPage.getByText("You don't have any alerts yet"),
      ).toBeVisible();
    });
  });
});

async function assertRowValues(
  formLocator: Locator,
  rowIndex: number,
  values: string[],
) {
  for (let i = 0; i < values.length; i++) {
    const column = formLocator.getByRole("row").nth(i);
    const cell = column.getByLabel("Preview cell").nth(rowIndex);
    await expect(cell).toHaveText(values[i]);
  }
}
